// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#include <melon/thread_local.h>

#include <pollux/common/base/exceptions.h>
#include <pollux/common/base/runtime_metrics.h>
#include <pollux/common/base/succinct_printer.h>

namespace kumo::pollux {
    void RuntimeMetric::addValue(int64_t value) {
        sum += value;
        count++;
        min = std::min(min, value);
        max = std::max(max, value);
    }

    void RuntimeMetric::aggregate() {
        count = std::min(count, static_cast<int64_t>(1));
        min = max = sum;
    }

    void RuntimeMetric::merge(const RuntimeMetric &other)
#if defined(__has_feature)
#if __has_feature(__address_sanitizer__)
    __attribute__((__no_sanitize__("signed-integer-overflow")))
#endif
#endif
    {
        POLLUX_CHECK_EQ(unit, other.unit);
        sum += other.sum;
        count += other.count;
        min = std::min(min, other.min);
        max = std::max(max, other.max);
    }

    void RuntimeMetric::printMetric(std::stringstream &stream) const {
        switch (unit) {
            case RuntimeCounter::Unit::kNanos:
                stream << " sum: " << succinctNanos(sum) << ", count: " << count
                        << ", min: " << succinctNanos(min)
                        << ", max: " << succinctNanos(max)
                        << ", avg: " << succinctNanos(count == 0 ? 0 : sum / count);
                break;
            case RuntimeCounter::Unit::kBytes:
                stream << " sum: " << succinctBytes(sum) << ", count: " << count
                        << ", min: " << succinctBytes(min)
                        << ", max: " << succinctBytes(max)
                        << ", avg: " << succinctBytes(count == 0 ? 0 : sum / count);
                break;
            case RuntimeCounter::Unit::kNone:
                [[fallthrough]];
            default:
                stream << " sum: " << sum << ", count: " << count << ", min: " << min
                        << ", max: " << max << ", avg: " << (count == 0 ? 0 : sum / count);
        }
    }

    std::string RuntimeMetric::toString() const {
        switch (unit) {
            case RuntimeCounter::Unit::kNanos:
                return fmt::format(
                    "sum:{}, count:{}, min:{}, max:{}, avg: {}",
                    succinctNanos(sum),
                    count,
                    succinctNanos(min),
                    succinctNanos(max),
                    succinctNanos(count == 0 ? 0 : sum / count));
                break;
            case RuntimeCounter::Unit::kBytes:
                return fmt::format(
                    "sum:{}, count:{}, min:{}, max:{}, avg: {}",
                    succinctBytes(sum),
                    count,
                    succinctBytes(min),
                    succinctBytes(max),
                    succinctBytes(count == 0 ? 0 : sum / count));
                break;
            case RuntimeCounter::Unit::kNone:
                [[fallthrough]];
            default:
                return fmt::format(
                    "sum:{}, count:{}, min:{}, max:{}, avg: {}",
                    sum,
                    count,
                    min,
                    max,
                    count == 0 ? 0 : sum / count);
        }
    }

    // Thread local runtime stat writers.
    static thread_local BaseRuntimeStatWriter *localRuntimeStatWriter;

    void setThreadLocalRunTimeStatWriter(BaseRuntimeStatWriter *writer) {
        localRuntimeStatWriter = writer;
    }

    BaseRuntimeStatWriter *getThreadLocalRunTimeStatWriter() {
        return localRuntimeStatWriter;
    }

    void addThreadLocalRuntimeStat(
        const std::string &name,
        const RuntimeCounter &value) {
        if (localRuntimeStatWriter) {
            localRuntimeStatWriter->addRuntimeStat(name, value);
        }
    }
} // namespace kumo::pollux
